package com.yihuchess.simplechess;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;

import com.yihuchess.bluetoothConnect.BtInterfaceMananger;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by cheason on 17-12-21.
 */

public class BoardView extends SurfaceView implements SurfaceHolder.Callback {
    private static final String TAG = "BoardView";

    private static final int START_Y = 10;

    public static final int BOARD_WIDTH = 8;
    public static final int BOARD_HEIGHT = 8;

    private ImageView mAnimationView;

    private int mWidth;
    private int mHeight;
    private float mGridWidth;
    private float mGridHeight;

    private GestureDetector mGestureDetector;
    private BoardGestureListener mGestureListener;
    protected Handler mUiHandler;
    private Bitmap mBoardBmp;
    private Bitmap mBoardBackgroundBmp;
    private Bitmap mSelectBmp;
    private boolean mAnimationing = false;

    private Point mSelectedPoint;
    private Point mMoveFromPoint, mMoveToPoint;

    public Board mGameboard;
    public static String thirdArgument = null; //TODO: show pawn promotion selection dialog

    protected static Map<String, Bitmap> sBitmaps = new HashMap<String, Bitmap>();

    private static final Paint sXferModePaintSrc;
    private static final Paint sXferModePaintAtop;
    private static final Paint sXferModePaintAtopAlpha;
    static {
        sXferModePaintSrc = new Paint();
        sXferModePaintAtop = new Paint();
        sXferModePaintSrc.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
        sXferModePaintAtop.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
        sXferModePaintAtopAlpha = new Paint(sXferModePaintAtop);
    }

    public BoardView(Context context, AttributeSet attrs) {
        super(context, attrs);

        SurfaceHolder holder = getHolder();
        holder.setFormat(PixelFormat.TRANSPARENT);
        holder.addCallback(this);

        mUiHandler = new Handler();

        mGestureListener = new BoardGestureListener();
        mGestureDetector = new GestureDetector(context, mGestureListener);

        mGameboard = new Board();
    }

    private Bitmap createScaleBitmap(int id, float dstWidth, float dstHeight) {
        Bitmap bitmap = Bitmap.createBitmap((int)dstWidth, (int)dstHeight, Bitmap.Config.ARGB_8888);

        Drawable drawable = getContext().getResources().getDrawable(id, null);
        int drawableWidth = drawable.getIntrinsicWidth();
        int drawableHeight = drawable.getIntrinsicHeight();
        drawable.setBounds(0, 0, drawableWidth, drawableHeight);

        Canvas canvas = new Canvas(bitmap);
        canvas.scale((float) dstWidth / drawableWidth, (float) dstHeight / drawableHeight);
        drawable.draw(canvas);
        return bitmap;
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        mWidth = width;
        mHeight = height;

        mGridWidth = width/BOARD_WIDTH;
        mGridHeight = width/BOARD_HEIGHT;

        mBoardBmp = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
        mBoardBackgroundBmp = createScaleBitmap(R.drawable.board, mWidth, mWidth);

        sBitmaps.put("bB", createScaleBitmap(R.drawable.black_bishop, mGridWidth, mGridHeight));
        sBitmaps.put("bK", createScaleBitmap(R.drawable.black_king, mGridWidth, mGridHeight));
        sBitmaps.put("bN", createScaleBitmap(R.drawable.black_knight, mGridWidth, mGridHeight));
        sBitmaps.put("bP", createScaleBitmap(R.drawable.black_pawn, mGridWidth, mGridHeight));
        sBitmaps.put("bQ", createScaleBitmap(R.drawable.black_queen, mGridWidth, mGridHeight));
        sBitmaps.put("bR", createScaleBitmap(R.drawable.black_rook, mGridWidth, mGridHeight));
        sBitmaps.put("wB", createScaleBitmap(R.drawable.white_bishop, mGridWidth, mGridHeight));
        sBitmaps.put("wK", createScaleBitmap(R.drawable.white_king, mGridWidth, mGridHeight));
        sBitmaps.put("wN", createScaleBitmap(R.drawable.white_knight, mGridWidth, mGridHeight));
        sBitmaps.put("wP", createScaleBitmap(R.drawable.white_pawn, mGridWidth, mGridHeight));
        sBitmaps.put("wQ", createScaleBitmap(R.drawable.white_queen, mGridWidth, mGridHeight));
        sBitmaps.put("wR", createScaleBitmap(R.drawable.white_rook, mGridWidth, mGridHeight));

        mSelectBmp = createScaleBitmap(R.drawable.select, mGridWidth+4, mGridHeight+4);

        ViewGroup parent = (ViewGroup)getParent();
        mAnimationView = (ImageView)parent.findViewById(R.id.animationView);
        drawBoard();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return mGestureDetector.onTouchEvent(event);
    }

    private void drawBoard2Surface() {
        Canvas surfaceCanvas = getHolder().lockCanvas();
        if (surfaceCanvas != null) {
            surfaceCanvas.drawBitmap(mBoardBmp, 0, 0, sXferModePaintSrc);
            getHolder().unlockCanvasAndPost(surfaceCanvas);
        }
    }

    private void drawNumber(Canvas canvas) {
        final String[] rowNum = {"1", "2", "3", "4", "5", "6", "7", "8"};
        final String[] columnNum = {"a", "b", "c", "d", "e", "f", "g", "h"};
        float x, y;

        sXferModePaintAtop.setAlpha(0xff);
        sXferModePaintAtop.setTextSize(22);
        sXferModePaintAtop.setFakeBoldText(true);

        // column
        x = 0;
        y = START_Y + (mGridHeight*BOARD_HEIGHT);
        for(int i = 0; i < BOARD_WIDTH; i++) {
            canvas.drawText(columnNum[i], x+4, y-16, sXferModePaintAtop);
            x += mGridWidth;
        }

        // row
        x = mGridWidth*BOARD_WIDTH;
        y = START_Y + BOARD_HEIGHT;
        for(int i = 0; i < BOARD_HEIGHT; i++) {
            canvas.drawText(rowNum[BOARD_HEIGHT-1-i], x-16, y+4, sXferModePaintAtop);
            y += mGridHeight;
        }
    }

    private void drawBoard() {
        if(mAnimationing)
            return;
        Canvas canvas = new Canvas(mBoardBmp);
        canvas.drawBitmap(mBoardBackgroundBmp, 0, 0, sXferModePaintSrc);
        drawNumber(canvas);

        Point fromPoint, toPoint;
        fromPoint = mMoveFromPoint;
        toPoint = mMoveToPoint;

        for(int y = 0; y < BOARD_HEIGHT; y++) {
            for(int x = 0; x < BOARD_WIDTH; x++) {
                if(mGameboard.board[x][y] == null)
                    continue;
                sXferModePaintAtop.setAlpha(0xff);
                Bitmap bmp = sBitmaps.get(mGameboard.board[x][y].getName());
                float posx = mGridWidth*x;
                float posy = START_Y + mGridHeight*y;
                if(bmp != null) {
                    canvas.drawBitmap(bmp, posx, posy-10, sXferModePaintAtop);
                    if(mSelectedPoint != null && x == mSelectedPoint.x  && y == mSelectedPoint.y)
                        canvas.drawBitmap(mSelectBmp, posx-2, posy-12, sXferModePaintAtopAlpha);
                    if(toPoint != null && x == toPoint.x  && y == toPoint.y)
                        canvas.drawBitmap(mSelectBmp, posx-2, posy-12, sXferModePaintAtopAlpha);
                }
                if(fromPoint != null && x == fromPoint.x  && y == fromPoint.y)
                    canvas.drawBitmap(mSelectBmp, posx-2, posy-12, sXferModePaintAtopAlpha);
            }
        }
        drawBoard2Surface();
    }

    private void endAnimation() {
        mAnimationing = false;
        drawBoard();
    }

    private void drawBoardAnimation() {
        Point fromPoint, toPoint;
        fromPoint = mMoveFromPoint;
        toPoint = mMoveToPoint;

        if(fromPoint == null || toPoint == null) {
            endAnimation();
            return;
        }

        Bitmap chessBitmap = null;
        mAnimationing = true;

        // draw base
        Canvas canvas = new Canvas(mBoardBmp);
        canvas.drawBitmap(mBoardBackgroundBmp, 0, 0, sXferModePaintSrc);
        drawNumber(canvas);

        for(int y = 0; y < BOARD_HEIGHT; y++) {
            for(int x = 0; x < BOARD_WIDTH; x++) {
                if(mGameboard.board[x][y] == null)
                    continue;
                sXferModePaintAtop.setAlpha(0xff);
                Bitmap bmp = sBitmaps.get(mGameboard.board[x][y].getName());
                float posx = mGridWidth*x;
                float posy = START_Y + mGridHeight*y;
                if(bmp != null) {
                    if(x == toPoint.x  && y == toPoint.y) {
                        chessBitmap = bmp;
                    } else {
                        canvas.drawBitmap(bmp, posx, posy-10, sXferModePaintAtop);
                    }
                }
                if(x == fromPoint.x  && y == fromPoint.y)
                    canvas.drawBitmap(mSelectBmp, posx-2, posy-10, sXferModePaintAtopAlpha);
            }
        }

        // draw animation
        final Bitmap animBitmap = chessBitmap;
        if(chessBitmap == null || mAnimationView == null) {
            endAnimation();
            return;
        }
        drawBoard2Surface();

        mUiHandler.post(new Runnable() {
            @Override
            public void run() {
                mAnimationView.setVisibility(View.VISIBLE);
                Animation anim = new TranslateAnimation(
                        mGridWidth*mMoveFromPoint.x,
                        mGridWidth*mMoveToPoint.x,
                        START_Y + mGridHeight*mMoveFromPoint.y,
                        START_Y + mGridHeight*mMoveToPoint.y);
                anim.setDuration(300);
                anim.setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {

                    }

                    @Override
                    public void onAnimationEnd(Animation animation) {
                        if(mAnimationView.getVisibility() == View.VISIBLE) {
                            mAnimationView.setVisibility(View.GONE);
                            endAnimation();
                        }
                    }

                    @Override
                    public void onAnimationRepeat(Animation animation) {

                    }
                });
                mAnimationView.setImageBitmap(animBitmap);
                mAnimationView.startAnimation(anim);
            }
        });
    }

    public void pawnPromotion(int newx, int newy) {
        if (mGameboard.board[newx][newy].getName().equalsIgnoreCase("wp") && newy == 0) {
            if (thirdArgument == null) {
                mGameboard.board[newx][newy] = new Queen(true);
            } else if (thirdArgument.equals("q")) {
                mGameboard.board[newx][newy] = new Queen(true);
            } else if (thirdArgument.equals("r")) {
                mGameboard.board[newx][newy] = new Rook(true);
            } else if (thirdArgument.equals("b")) {
                mGameboard.board[newx][newy] = new Bishop(true);
            } else if (thirdArgument.equals("n")) {
                mGameboard.board[newx][newy] = new Knight(true);
            }
        } else if (mGameboard.board[newx][newy].getName().equalsIgnoreCase("bp") && newy == 7) {
            if (thirdArgument == null) {
                mGameboard.board[newx][newy] = new Queen(false);
            } else if (thirdArgument.equals("q")) {
                mGameboard.board[newx][newy] = new Queen(false);
            } else if (thirdArgument.equals("r")) {
                mGameboard.board[newx][newy] = new Rook(false);
            } else if (thirdArgument.equals("b")) {
                mGameboard.board[newx][newy] = new Bishop(false);
            } else if (thirdArgument.equals("n")) {
                mGameboard.board[newx][newy] = new Knight(false);
            }
        }
    }

    public void tryMove(Point from, Point to) {
        boolean isNewSpotEmpty = true;
        if (mGameboard.board[to.x][to.y] != null) {
            isNewSpotEmpty = false;
        }

        if (mGameboard.enPassant == true) {
            mGameboard.enPassant = false;
            if (mGameboard.testEnPassant(from.x, from.y, to.x, to.y) == true) {
                //gameboard.detectCheckMate();

                //no longer the first move of the piece
                mGameboard.board[to.x][to.y].firstMove = false;
                mGameboard.mWhiteTurn = !mGameboard.mWhiteTurn;

                BtInterfaceMananger.selectReadyAndGo(from.x, from.y, to.x, to.y);
                return;
            }
        }

        if (mGameboard.isPathClear(from.x, from.y, to.x, to.y) == false) {
            System.out.println("Cannot move to that location because there are pieces on the way.");
            return;
        }

        if (mGameboard.board[from.x][from.y].canMove(from.x, from.y, to.x, to.y, isNewSpotEmpty) == false) {
            System.out.println("Illegal move. This piece cannot move this way.");
            return;
        }


        mGameboard.board[to.x][to.y] = mGameboard.board[from.x][from.y];//move the piece to the new location
        //no longer the first move of the piece
        mGameboard.board[to.x][to.y].firstMove = false;

        if (mGameboard.board[to.x][to.y].getName().equalsIgnoreCase("wp") || mGameboard.board[to.x][to.y].getName().equalsIgnoreCase("bp")) {
            pawnPromotion(to.x, to.y);
        }

        mGameboard.board[from.x][from.y] = null;
        mMoveFromPoint = from;
        mMoveToPoint = to;

        //detect enpassant scenario for the next turn
        if (mGameboard.detectEnPassant(from.x, from.y, to.x, to.y)) {
            System.out.println("en passant");
            //	System.out.println("enPassantx:"+enPassantX);
            //	System.out.println("enPassanty:"+enPassantY);
        }


        //mGameboard.detectCheckMate();
        mGameboard.mWhiteTurn = !mGameboard.mWhiteTurn;
        drawBoardAnimation();
        mSelectedPoint = null;

        BtInterfaceMananger.selectReadyAndGo(from.x, from.y, to.x, to.y);
    }

    private Point coord2Point(float x, float y) {
        int px = (int)(x/mGridWidth);
        int py = (int)((y - START_Y)/mGridHeight);

        if(px < 0 || py < 0)
            return null;
        return new Point(px, py);
    }

    private boolean isSameColor(Piece p1, Piece p2) {
        if(p1 == null || p2 == null)
            return false;
        return ((p1.isWhite() && p2.isWhite() || (!p1.isWhite() && !p2.isWhite())));
    }

    private final class BoardGestureListener
            extends GestureDetector.SimpleOnGestureListener {

        @Override
        public boolean onDown(MotionEvent ev) {
            return onScroll(null, ev, 0, 0);
        }

        @Override
        public boolean onScroll(MotionEvent ev1,
                                MotionEvent ev2, float dx, float dy) {
            return true;
        }

        @Override
        public boolean onSingleTapUp(MotionEvent ev) {
            Point mP = coord2Point(ev.getX() - getX(), ev.getY() - getY());
            if(mP == null)
                return false;
            else
                return onePieceSelect(mP);
        }
    }


    public boolean onePieceSelect(Point p) {
        Piece piece = mGameboard.board[p.x][p.y];

        if(mSelectedPoint != null) {
            Piece piece1 = mGameboard.board[mSelectedPoint.x][mSelectedPoint.y];
            if(isSameColor(piece, piece1)) {
                if(mGameboard.testCastling(mSelectedPoint.x, mSelectedPoint.y, p.x, p.y)) {
                    mGameboard.mWhiteTurn = !mGameboard.mWhiteTurn;
                    mSelectedPoint = null;
                    drawBoard();
                    return true;
                } else {
                    mSelectedPoint = p;     // select another piece
                }
            } else {
                tryMove(mSelectedPoint, p);
            }
        } else {
            if(piece == null || (mGameboard.mWhiteTurn && !piece.isWhite()) || (!mGameboard.mWhiteTurn && piece.isWhite())) {
                // click illegal color
                Log.d(TAG, "click illegal color piece or empty grid");
                return false;
            }
            Log.d(TAG, "click " + p.toString());
            mSelectedPoint = p;
        }

        drawBoard();
        return true;
    }

    public boolean onePieceMove(int p_x, int p_y) {
        Point mP = new Point(p_x, p_y);
        return onePieceSelect(mP);
    }

}

